<template>
  <div class="account-edit">
    <div class="page-header">
      <div class="header-left">
        <h3>{{ isEdit ? '编辑' : '新建' }}账号</h3>
        <p>管理Web3账号的基础信息</p>
      </div>
      <div class="header-right">
        <el-button v-if="isEdit" type="primary" @click="handleManageConfigs">
          <el-icon><Setting /></el-icon>
          配置管理
        </el-button>
        <el-button @click="handleCancel">取消</el-button>
        <el-button type="primary" :loading="saving" @click="handleSave">
          {{ saving ? '保存中...' : '保存' }}
        </el-button>
      </div>
    </div>

    <div class="edit-content">
      <el-card>
        <el-form
          ref="accountFormRef"
          :model="accountForm"
          :rules="accountRules"
          label-width="120px">

          <el-form-item label="账号名称" prop="name">
            <el-input v-model="accountForm.name" placeholder="请输入账号名称" />
          </el-form-item>

          <el-form-item label="账号状态" prop="status">
            <el-radio-group v-model="accountForm.status">
              <el-radio :label="1">启用</el-radio>
              <el-radio :label="0">禁用</el-radio>
            </el-radio-group>
          </el-form-item>

          <el-form-item label="Cookies">
            <el-input
              v-model="accountForm.cookies"
              type="textarea"
              :rows="4"
              placeholder="请输入Cookies信息"
              show-word-limit />
          </el-form-item>

          <el-form-item label="CSRF Token">
            <el-input v-model="accountForm.csrfToken" placeholder="请输入CSRF Token" />
          </el-form-item>

          <el-form-item label="代理配置">
            <ProxyConfigEditor v-model="accountForm.proxyConfig" />
          </el-form-item>

          <el-form-item label="备注">
            <el-input
              v-model="accountForm.remark"
              type="textarea"
              :rows="3"
              placeholder="请输入备注信息" />
          </el-form-item>
        </el-form>
      </el-card>

      <el-card v-if="isEdit" class="token-card">
        <div class="token-header">
          <div class="token-title">
            <h4>客户端访问Token</h4>
            <p>为 auto-trade-client 提供长期有效的鉴权凭证</p>
          </div>
          <el-button type="primary" :loading="tokenGenerating" @click="handleGenerateToken">
            {{ tokenGenerating ? '生成中...' : '生成Token' }}
          </el-button>
        </div>

        <el-descriptions :column="1" size="small" border>
          <el-descriptions-item label="当前状态">
            <el-tag :type="clientToken.hasToken ? 'success' : 'info'">
              {{ clientToken.hasToken ? '已生成' : '未生成' }}
            </el-tag>
          </el-descriptions-item>
          <el-descriptions-item label="到期时间">
            {{ clientToken.hasToken ? (clientToken.expiresAt || '未设置') : '生成后可见' }}
          </el-descriptions-item>
        </el-descriptions>

        <el-alert
          v-if="clientToken.lastToken"
          class="token-alert"
          type="success"
          :closable="false"
          show-icon>
          <template #title>
            新客户端Token（请立即妥善保存）
          </template>
          <div class="token-value">
            <span>{{ clientToken.lastToken }}</span>
            <el-button type="primary" link @click="handleCopyToken">复制Token</el-button>
          </div>
          <div class="launch-command">
            <div class="command-title">
              <span>客户端启动命令：</span>
              <el-button type="primary" link @click="handleCopyLaunchCommand">复制命令</el-button>
            </div>
            <div class="command-value">
              <code>{{ generateLaunchCommand() }}</code>
            </div>
          </div>
          <p class="token-hint">该Token仅显示一次，刷新页面后将无法再次查看原文，若遗失请重新生成。</p>
        </el-alert>
      </el-card>

      <el-card v-if="isEdit" class="tm-card">
        <div class="tm-header">
          <div class="tm-title">
            <h4>Tampermonkey 客户端</h4>
            <p>绑定浏览器脚本以自动采集二维码与登录信息</p>
          </div>
          <div class="tm-actions">
            <el-button type="primary" link @click="loadCredentialLogs" :loading="credentialLogsLoading">
              刷新日志
            </el-button>
          </div>
        </div>

        <el-descriptions :column="2" border size="small" class="tm-descriptions">
          <el-descriptions-item label="绑定客户端">
            <template v-if="tmInfo.clientId">
              <span class="mono">{{ tmInfo.clientId }}</span>
              <el-tag size="small" :type="tmInfo.status === 'ACTIVE' ? 'success' : 'warning'">
                {{ tmInfo.status || '未知' }}
              </el-tag>
            </template>
            <el-tag v-else type="info">未绑定</el-tag>
          </el-descriptions-item>
          <el-descriptions-item label="最后心跳">
            {{ formatDateTime(tmInfo.lastSeenAt) || '--' }}
          </el-descriptions-item>
          <el-descriptions-item label="凭证同步">
            {{ formatDateTime(tmInfo.lastCredentialAt) || '--' }}
          </el-descriptions-item>
          <el-descriptions-item label="二维码捕获">
            {{ formatDateTime(tmInfo.lastQrAt) || '--' }}
          </el-descriptions-item>
          <el-descriptions-item label="待处理指令">
            <el-tag size="small" :type="tmInfo.pendingCommands > 0 ? 'warning' : 'info'">
              {{ tmInfo.pendingCommands }}
            </el-tag>
          </el-descriptions-item>
        </el-descriptions>

        <div class="tm-bind">
          <el-input
            v-model="tmBind.clientId"
            class="tm-bind__input"
            placeholder="请输入客户端ID"
            clearable />
          <el-input
            v-model="tmBind.remark"
            class="tm-bind__input"
            placeholder="备注（可选）"
            clearable />
          <el-button type="primary" :loading="bindingClient" @click="handleBindClient">
            绑定客户端
          </el-button>
          <el-button
            v-if="tmInfo.clientId"
            type="danger"
            plain
            :loading="unbindingClient"
            @click="handleUnbindClient">
            解绑
          </el-button>
        </div>

        <div class="tm-command">
          <el-button
            type="warning"
            plain
            :loading="commandLoading.qr"
            @click="handleCreateCommand('REQUEST_QR')"
            :disabled="!tmInfo.clientId">
            请求二维码
          </el-button>
          <el-button
            type="success"
            plain
            :loading="commandLoading.session"
            @click="handleCreateCommand('REQUEST_SESSION')"
            :disabled="!tmInfo.clientId">
            同步登录信息
          </el-button>
        </div>

        <el-table
          :data="credentialLogs"
          size="small"
          border
          v-loading="credentialLogsLoading"
          empty-text="暂无上报记录"
          class="tm-log-table">
          <el-table-column prop="captureType" label="类型" width="100">
            <template #default="{ row }">
              <el-tag size="small" :type="row.captureType === 'SESSION' ? 'success' : 'warning'">
                {{ row.captureType === 'SESSION' ? '登录信息' : '二维码' }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="status" label="状态" width="100">
            <template #default="{ row }">
              <el-tag size="small" :type="row.status === 'SUCCESS' ? 'success' : 'danger'">
                {{ row.status }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="capturedAt" label="时间" width="180">
            <template #default="{ row }">
              {{ formatDateTime(row.capturedAt) }}
            </template>
          </el-table-column>
          <el-table-column prop="clientId" label="客户端" min-width="150">
            <template #default="{ row }">
              {{ row.clientId || '--' }}
            </template>
          </el-table-column>
          <el-table-column prop="summary" label="摘要" min-width="220" show-overflow-tooltip>
            <template #default="{ row }">
              {{ row.summary || row.message || '--' }}
            </template>
          </el-table-column>
          <el-table-column label="操作" width="120">
            <template #default="{ row }">
              <el-button type="primary" text size="small" @click="handleViewPayload(row)">
                查看
              </el-button>
            </template>
          </el-table-column>
        </el-table>

        <el-dialog
          v-model="payloadDialog.visible"
          :title="payloadDialog.title"
          width="560px"
          destroy-on-close>
          <div class="payload-dialog">
            <template v-if="payloadDialog.isQr && payloadDialog.qrUrl">
              <!-- 使用新的二维码组件 -->
              <div class="qr-display-new">
                <QrCodeDisplay
                  :qr-code-url="payloadDialog.qrUrl"
                  :original-qr-code="payloadDialog.raw?.originalQrCode"
                  :captured-at="payloadDialog.raw?.capturedAt" />
              </div>
            </template>
            <template v-else-if="payloadDialog.isQr">
              <div class="qr-preview" v-if="payloadDialog.qrSrc">
                <img :src="payloadDialog.qrSrc" alt="二维码截图" />
                <el-text size="small" type="info">页面截图二维码</el-text>
                <el-button type="primary" link @click="downloadQrImage">下载二维码图片</el-button>
              </div>
            </template>
            <pre class="payload-view">{{ payloadDialog.content }}</pre>
          </div>
          <template #footer>
            <el-button @click="payloadDialog.visible = false">关闭</el-button>
          </template>
        </el-dialog>
      </el-card>
    </div>
  </div>
</template>

<script setup>
import { reactive, ref, computed, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { http } from '@/utils/request'
import { ElMessage, ElMessageBox } from 'element-plus'
import ProxyConfigEditor from '@/components/ProxyConfigEditor.vue'
import QrCodeDisplay from '@/components/QrCodeDisplay.vue'

const route = useRoute()
const router = useRouter()

// 路由参数
const accountId = ref(route.params.id ? parseInt(route.params.id) : null)
const isEdit = computed(() => !!accountId.value)

// 表单引用
const accountFormRef = ref()

// 状态
const saving = ref(false)
const tokenGenerating = ref(false)

// 账号表单
const accountForm = reactive({
  name: '',
  status: 1,
  cookies: '',
  csrfToken: '',
  proxyConfig: '{}',
  remark: ''
})

const clientToken = reactive({
  hasToken: false,
  expiresAt: '',
  lastToken: ''
})

const tmInfo = reactive({
  clientId: '',
  status: '',
  lastSeenAt: '',
  lastCredentialAt: '',
  lastQrAt: '',
  pendingCommands: 0
})

const tmBind = reactive({
  clientId: '',
  remark: ''
})

const credentialLogs = ref([])
const credentialLogsLoading = ref(false)
const bindingClient = ref(false)
const unbindingClient = ref(false)
const commandLoading = reactive({
  qr: false,
  session: false
})
const payloadDialog = reactive({
  visible: false,
  title: '',
  content: '',
  isQr: false,
  qrSrc: '',
  qrUrl: '',
  raw: null
})

// 表单验证规则
const accountRules = {
  name: [
    { required: true, message: '请输入账号名称', trigger: 'blur' },
    { max: 100, message: '账号名称不能超过100个字符', trigger: 'blur' }
  ],
  status: [
    { required: true, message: '请选择账号状态', trigger: 'change' }
  ]
}

// 初始化
onMounted(async () => {
  if (isEdit.value) {
    await loadAccountDetail()
    await loadCredentialLogs()
  }
})

// 加载账号详情
const loadAccountDetail = async () => {
  try {
    const response = await http.get(`/accounts/${accountId.value}`)
    const detail = response.data

    Object.assign(accountForm, {
      name: detail.name,
      status: detail.status,
      cookies: detail.cookies || '',
      csrfToken: detail.csrfToken || '',
      proxyConfig: detail.proxyConfig || '{}',
      remark: detail.remark || ''
    })

    clientToken.hasToken = Boolean(detail.hasClientToken)
    clientToken.expiresAt = detail.clientTokenExpiresAt || ''
    clientToken.lastToken = ''

    tmInfo.clientId = detail.boundClientId || ''
    tmInfo.status = detail.clientStatus || ''
    tmInfo.lastSeenAt = detail.clientLastSeenAt || ''
    tmInfo.lastCredentialAt = detail.lastCredentialCapturedAt || ''
    tmInfo.lastQrAt = detail.lastQrCapturedAt || ''
    tmInfo.pendingCommands = detail.pendingCommandCount || 0

    tmBind.clientId = tmInfo.clientId
    tmBind.remark = ''
  } catch (error) {
    ElMessage.error('加载账号详情失败')
    router.back()
  }
}

const loadCredentialLogs = async () => {
  if (!isEdit.value) return
  credentialLogsLoading.value = true
  try {
    const response = await http.get(`/accounts/${accountId.value}/credential-logs`)
    credentialLogs.value = (response.data || []).map(transformCredentialLog)
  } catch (error) {
    ElMessage.error('加载客户端日志失败')
  } finally {
    credentialLogsLoading.value = false
  }
}

const transformCredentialLog = (log) => {
  const item = { ...log }
  if (log.payload) {
    try {
      const payload = JSON.parse(log.payload)
      if (log.captureType === 'SESSION') {
        item.summary = buildSessionSummary(payload)
      } else if (log.captureType === 'QR') {
        item.summary = buildQrSummary(payload)
      }
      item.prettyPayload = JSON.stringify(payload, null, 2)
      item.payloadObj = payload
      if (payload?.fetchSnapshot?.qrCode) {
        item.qrUrl = payload.fetchSnapshot.qrCode
      } else if (payload?.fetchSnapshot?.originalQrCode) {
        item.qrUrl = payload.fetchSnapshot.originalQrCode
      }
    } catch (error) {
      item.summary = log.message || 'payload 无法解析'
      item.prettyPayload = log.payload
    }
  } else {
    item.summary = log.message || ''
    item.prettyPayload = ''
  }
  return item
}

const buildSessionSummary = (payload) => {
  const parts = []
  if (payload?.cookies) {
    parts.push(`Cookies长度 ${payload.cookies.length}`)
  }
  const csrf = extractCsrfToken(payload)
  if (csrf) {
    parts.push(`CSRF=${csrf}`)
  }
  return parts.length ? parts.join(' / ') : '已上报登录信息'
}

const buildQrSummary = (payload) => {
  // 支持新的数据格式（v1.3优化后的格式）
  const qrCode = payload?.qrCode || payload?.fetchSnapshot?.qrCode || payload?.fetchSnapshot?.originalQrCode
  if (qrCode) {
    return `二维码链接：${qrCode}`
  }

  // 兼容旧的数据格式
  if (payload?.domSnapshot?.dataUrl) {
    return '已捕获二维码 Canvas'
  }

  return '已捕获二维码'
}

const extractCsrfToken = (payload) => {
  if (!payload) return ''
  if (payload.cookies) {
    const token = parseCookieToken(payload.cookies)
    if (token) return token
  }
  if (payload.headersByUrl) {
    for (const key of Object.keys(payload.headersByUrl)) {
      const headers = payload.headersByUrl[key]?.headers
      if (!headers) continue
      for (const headerKey of Object.keys(headers)) {
        const lower = headerKey.toLowerCase()
        if (lower.includes('csrf')) {
          return headers[headerKey]
        }
      }
    }
  }
  if (payload.storageItems) {
    for (const item of payload.storageItems) {
      if (item?.key && item?.value && item.key.toLowerCase().includes('csrf')) {
        return item.value
      }
    }
  }
  return ''
}

const parseCookieToken = (cookieString) => {
  return cookieString
    .split(';')
    .map((item) => item.trim())
    .map((pair) => pair.split('='))
    .find(([name]) => name && name.toLowerCase().includes('csrf'))?.[1] || ''
}

// 保存账号
const handleSave = async () => {
  if (!accountFormRef.value) return

  try {
    await accountFormRef.value.validate()
    saving.value = true

    if (isEdit.value) {
      await http.put(`/accounts/${accountId.value}`, accountForm)
      ElMessage.success('账号更新成功')
    } else {
      await http.post('/accounts', accountForm)
      ElMessage.success('账号创建成功')
    }

    router.back()
  } catch (error) {
    if (error.message) {
      ElMessage.error(error.message)
    }
  } finally {
    saving.value = false
  }
}

const handleBindClient = async () => {
  if (!accountId.value) return
  if (!tmBind.clientId?.trim()) {
    ElMessage.warning('请输入客户端ID')
    return
  }
  bindingClient.value = true
  try {
    await http.post(`/accounts/${accountId.value}/clients`, {
      clientId: tmBind.clientId.trim(),
      remark: tmBind.remark || undefined
    })
    ElMessage.success('绑定客户端成功')
    await loadAccountDetail()
    await loadCredentialLogs()
  } catch (error) {
    if (error.message) {
      ElMessage.error(error.message)
    }
  } finally {
    bindingClient.value = false
  }
}

const handleUnbindClient = async () => {
  if (!accountId.value || !tmInfo.clientId) return
  try {
    await ElMessageBox.confirm('确定要解绑当前客户端吗？', '确认解绑', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    })
  } catch (error) {
    return
  }

  unbindingClient.value = true
  try {
    await http.delete(`/accounts/${accountId.value}/clients/${tmInfo.clientId}`)
    ElMessage.success('客户端已解绑')
    await loadAccountDetail()
    await loadCredentialLogs()
  } catch (error) {
    if (error.message) {
      ElMessage.error(error.message)
    }
  } finally {
    unbindingClient.value = false
  }
}

const handleCreateCommand = async (type) => {
  if (!accountId.value) return
  if (!tmInfo.clientId) {
    ElMessage.warning('请先绑定客户端')
    return
  }
  const key = type === 'REQUEST_QR' ? 'qr' : 'session'
  commandLoading[key] = true
  try {
    await http.post(`/accounts/${accountId.value}/commands`, { type })
    ElMessage.success(type === 'REQUEST_QR' ? '已下发二维码请求' : '已下发登录信息请求')
    tmInfo.pendingCommands += 1
  } catch (error) {
    if (error.message) {
      ElMessage.error(error.message)
    }
  } finally {
    commandLoading[key] = false
    await loadCredentialLogs()
  }
}

const handleViewPayload = (row) => {
  if (!row) return
  const payloadObj = row.payloadObj || safeParse(row.payload)
  payloadDialog.title = row.captureType === 'QR' ? '二维码上报内容' : '登录信息上报内容'
  payloadDialog.content = row.prettyPayload || row.payload || '无内容'
  payloadDialog.isQr = row.captureType === 'QR'
  payloadDialog.qrSrc = ''
  payloadDialog.qrUrl = ''

  if (payloadDialog.isQr && payloadObj) {
    // 支持新的数据格式（v1.3优化后的格式）
    payloadDialog.qrUrl = payloadObj?.qrCode || payloadObj?.fetchSnapshot?.qrCode || payloadObj?.fetchSnapshot?.originalQrCode || row.qrUrl || ''

    // 兼容旧的数据格式
    payloadDialog.qrSrc = payloadObj?.domSnapshot?.dataUrl || payloadObj?.fetchSnapshot?.qrImageBase64 || ''
  }
  payloadDialog.raw = payloadObj
  payloadDialog.visible = true
}

const escapeHtml = (str) => {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/\"/g, '&quot;')
    .replace(/'/g, '&#39;')
}

const safeParse = (text) => {
  if (!text) return null
  try {
    return JSON.parse(text)
  } catch (error) {
    return null
  }
}

const downloadQrImage = () => {
  if (!payloadDialog.qrSrc) return
  const link = document.createElement('a')
  link.href = payloadDialog.qrSrc
  link.download = `binance-qr-${Date.now()}.png`
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)
}

const formatDateTime = (value) => {
  if (!value) return ''
  return new Date(value).toLocaleString('zh-CN')
}

// 取消编辑
const handleCancel = () => {
  router.back()
}

// 跳转到配置管理
const handleManageConfigs = () => {
  router.push(`/accounts/${accountId.value}/configs`)
}

const handleGenerateToken = async () => {
  if (!accountId.value) return
  try {
    tokenGenerating.value = true
    const response = await http.post(`/accounts/${accountId.value}/client-token`)
    const tokenData = response.data
    clientToken.hasToken = true
    clientToken.expiresAt = tokenData.expiresAt || ''
    clientToken.lastToken = tokenData.token || ''
    ElMessage.success('客户端Token生成成功')
  } catch (error) {
    if (error.message) {
      ElMessage.error(error.message)
    }
  } finally {
    tokenGenerating.value = false
  }
}

const handleCopyToken = async () => {
  if (!clientToken.lastToken) return
  try {
    if (!navigator?.clipboard) {
      throw new Error('clipboard-unsupported')
    }
    await navigator.clipboard.writeText(clientToken.lastToken)
    ElMessage.success('Token已复制到剪贴板')
  } catch (error) {
    ElMessage.error('复制失败，请手动选择复制')
  }
}

// 生成客户端启动命令
const generateLaunchCommand = () => {
  if (!clientToken.lastToken) return ''

  // 获取当前站点的基础URL
  const baseUrl = window.location.origin

  return `node auto-trade-client.js --token=${clientToken.lastToken} --baseUrl=${baseUrl}`
}

// 复制启动命令
const handleCopyLaunchCommand = async () => {
  const command = generateLaunchCommand()
  if (!command) return

  try {
    if (!navigator?.clipboard) {
      throw new Error('clipboard-unsupported')
    }
    await navigator.clipboard.writeText(command)
    ElMessage.success('启动命令已复制到剪贴板')
  } catch (error) {
    ElMessage.error('复制失败，请手动选择复制')
  }
}
</script>

<style lang="scss" scoped>
.account-edit {
  .page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
    padding: 20px;
    background: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);

    .header-left {
      h3 {
        margin: 0 0 5px;
        color: #303133;
        font-size: 18px;
        font-weight: 600;
      }

      p {
        margin: 0;
        color: #606266;
        font-size: 14px;
      }
    }
  }

  .edit-content {
    :deep(.el-card__body) {
      padding: 30px;
    }

    .token-card {
      margin-top: 24px;

      .token-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 16px;

        .token-title {
          h4 {
            margin: 0 0 4px;
            font-size: 16px;
            color: #303133;
          }

          p {
            margin: 0;
            font-size: 13px;
            color: #909399;
          }
        }
      }

      .token-alert {
        margin-top: 16px;

        .token-value {
          display: flex;
          align-items: center;
          gap: 12px;
          font-family: ui-monospace, SFMono-Regular, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
          word-break: break-all;
        }

        .launch-command {
          margin-top: 16px;

          .command-title {
            display: flex;
            align-items: center;
            gap: 8px;
            margin-bottom: 8px;
            font-size: 14px;
            font-weight: 500;
            color: #303133;
          }

          .command-value {
            padding: 12px;
            background-color: #f5f7fa;
            border: 1px solid #e4e7ed;
            border-radius: 6px;

            code {
              display: block;
              font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Courier New', monospace;
              font-size: 13px;
              color: #606266;
              word-break: break-all;
              white-space: pre-wrap;
            }
          }
        }

        .token-hint {
          margin: 12px 0 0;
          color: #909399;
          font-size: 12px;
        }
      }
    }

    .tm-card {
      margin-top: 24px;

      .tm-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 16px;

        .tm-title {
          h4 {
            margin: 0 0 4px;
            font-size: 16px;
            color: #303133;
            font-weight: 600;
          }

          p {
            margin: 0;
            font-size: 13px;
            color: #909399;
          }
        }
      }

      .tm-descriptions {
        margin-bottom: 16px;

        .mono {
          font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Courier New', monospace;
          font-size: 12px;
          color: #4c6ef5;
        }
      }

      .tm-bind {
        display: flex;
        flex-wrap: wrap;
        gap: 10px;
        margin-bottom: 16px;

        &__input {
          flex: 1;
          min-width: 220px;
        }
      }

      .tm-command {
        display: flex;
        gap: 12px;
        margin-bottom: 16px;
      }

      .tm-log-table {
        margin-top: 10px;
      }
    }
  }
}

.payload-dialog {
  display: flex;
  flex-direction: column;
  gap: 12px;

  .qr-preview {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;

    img {
      width: 296px;
      height: 296px;
      border: 1px solid #e4e4e4;
      border-radius: 6px;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
      object-fit: contain;
    }
  }
}

.payload-view {
  margin: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Courier New', monospace;
  font-size: 12px;
  line-height: 1.5;
  white-space: pre-wrap;
}
</style>
